/*
 * Copyright (c) 2009 Roman Savelyev
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "attachedengine.h"
#include "terminalproxy.h"
#include "drawingbuffer.h"
#include "conuisurfacelogger.h"

NONSHARABLE_CLASS(CDrawRegionObserver) : public CBase, public MConUiEngineObserver
    {
public:
    CDrawRegionObserver();
    TInt Error() const;
private: // MConUiEngineObserver
    void HandleEngineError(TInt aError);
private:
    TInt iError;
    };

CAttachedEngine* CAttachedEngine::NewL(const TDesC8& aTerminalType)
    {
    FL(_L("CAttachedTerminalProxy::NewL"));
    CAttachedEngine* self = new(ELeave) CAttachedEngine;
    CleanupStack::PushL(self);
    self->ConstructL(aTerminalType);
    CleanupStack::Pop(self);
    return self;
    }

CAttachedEngine::CAttachedEngine()
    {}

void CAttachedEngine::ConstructL(const TDesC8& aTerminalType)
    {
    FL(_L("CAttachedTerminalProxy::ConstructL"));
    iState = EConstructing;
    iTerminal = CTerminalProxy::NewL(aTerminalType, *this);
    iSize = iTerminal->SurfaceSize();
    DoSetCursorVisible(CursorVisible());
    ClearSurface();
    if (EConstructionFailure == iState)
        {
        User::Leave(KErrGeneral);
        }
    else
        {
        iState = EConstructedOk;
        }
    }

CAttachedEngine::~CAttachedEngine()
    {
    FL(_L("CAttachedTerminalProxy::~CAttachedTerminalProxy"));
    delete iTerminal;
    }

TSize CAttachedEngine::SurfaceSize()
    {
    return iSize;
    }

void CAttachedEngine::ClearSurface()
    {
    iTerminal->ClearSurface();
    SetPos(TPoint(0, 0));
    SetBackground(ConUiDef::EDefault);
    SetForeground(ConUiDef::EDefault);
    SetBold(EFalse);
    }

TPoint CAttachedEngine::CursorPos() const
    {
    return Pos();
    }

void CAttachedEngine::SetCursorPos(const TPoint& aPos)
    {
    SetPos(aPos);
    }

void CAttachedEngine::ReadKey(TConsoleKey& aKey, TRequestStatus& aStatus)
    {
    iTerminal->ReadKey(aKey, aStatus);
    }

void CAttachedEngine::ReadKeyCancel()
    {
    iTerminal->ReadKeyCancel();
    }

void CAttachedEngine::DrawRegionL(const CDrawingBuffer& aBuffer, const RRegion& aRegion)
    {
    FL(_L("CAttachedEngine::DrawRegionL"));
    CDrawRegionObserver observer;
    PushObserverL(observer);
    CleanupStack::PushL(TCleanupItem(CleanupPopObserver, this));
    TPoint oldCursorPos = CursorPos();
    TBool oldVisibility = CursorVisible();
    SetCursorVisible(EFalse);
    aBuffer.UpdateDrawerL(*this, aRegion);
    SetCursorPos(oldCursorPos);
    SetCursorVisible(oldVisibility);
    CleanupStack::PopAndDestroy(this);
    User::LeaveIfError(observer.Error());
    }

void CAttachedEngine::DoSetCursorVisible(TBool aVisible)
    {
    if (aVisible)
        {
        iTerminal->ShowCursor();
        }
    else
        {
        iTerminal->HideCursor();
        }
    }

ConUiDef::TColor CAttachedEngine::Background() const
    {
    return iBackground;
    }

void CAttachedEngine::SetBackground(ConUiDef::TColor aColor)
    {
    iBackground = aColor;
    iTerminal->SetBackground(aColor);
    }

ConUiDef::TColor CAttachedEngine::Foreground() const
    {
    return iForeground;
    }

void CAttachedEngine::SetForeground(ConUiDef::TColor aColor)
    {
    iForeground = aColor;
    iTerminal->SetForeground(aColor);
    }

TBool CAttachedEngine::Bold() const
    {
    return iBold;
    }

void CAttachedEngine::SetBold(TBool aBold)
    {
    iBold = aBold;
    iTerminal->SetBold(aBold);
    }

TBool CAttachedEngine::InverseVideo() const
    {
    return iInverseVideo;
    }

void CAttachedEngine::SetInverseVideo(TBool aInverseVideo)
    {
    iInverseVideo = aInverseVideo;
    iTerminal->SetInverseVideo(aInverseVideo);
    }

TPoint CAttachedEngine::Pos() const
    {
    return iPos;
    }

void CAttachedEngine::SetPos(const TPoint& aPos)
    {
    TPoint pos = aPos;
    // cant be negative
    pos.iX = Max(0, pos.iX);
    pos.iY = Max(0, pos.iY);
    // cant be outside terminal bounds
    pos.iX = Min(SurfaceSize().iWidth - 1, pos.iX);
    pos.iY = Min(SurfaceSize().iHeight - 1, pos.iY);
    if (iPos != pos)
        {
        iPos = pos;
        iTerminal->SetPos(pos);
        }
    }

void CAttachedEngine::Write(const TDesC& aOutput)
    {
    TInt xPos = Pos().iX;
    xPos += aOutput.Length();
    TPtrC ptrOutput = aOutput.Left(xPos - Pos().iX);
    iTerminal->Write(ptrOutput);
    iPos.iX = xPos;
    }

void CAttachedEngine::HandleTerminalError(TInt aError)
    {
    FL(_L("CAttachedEngine::HandleTerminalError"));
    FLTRACEVALUE(iState);
    if (EConstructing == iState)
        {
        iState = EConstructionFailure;
        }
    else if (EConstructedOk == iState && Observer())
        {
        Observer()->HandleEngineError(aError);
        }
    // dont do anything if failure already caught in
    // ConstructL
    }

void CAttachedEngine::CleanupPopObserver(TAny* aSelf)
    {
    CAttachedEngine* self = reinterpret_cast<CAttachedEngine*>(aSelf);
    self->PopObserver();
    }
///////////////////////////////////////////////////////////////////////////////
CDrawRegionObserver::CDrawRegionObserver() :
    iError(KErrNone)
    {}

TInt CDrawRegionObserver::Error() const
    {
    return iError;
    }

void CDrawRegionObserver::HandleEngineError(TInt aError)
    {
    FL(_L("CDrawRegionObserver::HandleEngineError"));
    FLTRACEVALUE(aError);
    iError = aError;
    }
